/*
 * AbstractDisposable.java  0.4.0 / Oct 23, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.core;

import com.cute4j.Cute4j;
import com.cute4j.core.events.DisposeEvent;
import com.cute4j.core.events.DisposeListener;

/**
 * Abstract disposable object.
 *
 * @cute4j.native
 *    generate="jni"
 *    includes="QObjectDelegate.h"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public abstract class AbstractDisposable implements Disposable {

// Static Initialization. ------------------------------------------------------

  static {
    Cute4j.initialize();
    _initialize();
  }

// Private Fields. -------------------------------------------------------------

  private boolean disposed;
  private DisposeListener[] listeners;
  private int listenerCount;

// Protected Constructors. -----------------------------------------------------

  /**
   * Constructs new AbstractDisposable object.
   */
  protected AbstractDisposable() {
    listeners = new DisposeListener[3];
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Adds new dispose listener.
   *
   * @param listener the new listener
   */
  public void addDisposeListener(DisposeListener listener) {
    disposedCheck();
    if (listener != null) {
      int oldCount = listeners.length;
      if (listenerCount == oldCount) {
        final DisposeListener[] oldListeners = listeners;
        int length = (3 * oldCount) >> 1;
        listeners = new DisposeListener[++length];
        System.arraycopy(oldListeners, 0, listeners, 0, oldCount);
      }
      listeners[listenerCount++] = listener;
    }
  }

  /**
   * Removes dispose listener.
   *
   * @param listener the listener to be removed
   */
  public void removeDisposeListener(DisposeListener listener) {
    disposedCheck();
    if (listener != null) {
      final DisposeListener[] listeners = this.listeners;
      for (int index = listenerCount; --index >= 0;) {
        if (listeners[index] == listener) {
          int numMoved = listenerCount - index - 1;
          int halfCapacity = listeners.length >> 1;
          if (listenerCount <= halfCapacity) {
            this.listeners = new DisposeListener[halfCapacity];
            if (numMoved > 0) {
              System.arraycopy(listeners, 0, this.listeners, 0, index);
              System.arraycopy(listeners, index + 1, this.listeners, index,
                               numMoved);
              listenerCount--;
            } else {
              System.arraycopy(listeners, 0, this.listeners, 0,
                               --listenerCount);
            }
          } else {
            if (numMoved > 0) {
              System.arraycopy(listeners, index + 1, listeners, index,
                               numMoved);
            }
            listeners[--listenerCount] = null;
          }
          break;
        }
      }
    }
  }

// Protected Methods. ----------------------------------------------------------

  /**
   * Checks if the object is alive.
   */
  protected final void disposedCheck() {
    if (disposed) {
      throw new DisposedError(disposedErrorString());
    }
  }

  /**
   * Returns object disposed error message.
   *
   * @return object disposed error message
   */
  protected String disposedErrorString() {
    return "Object disposed: " + toString();
  }

// Protected Abstract Methods. -------------------------------------------------

  /**
   * Disposes the object.
   */
  protected abstract void disposeObject();

// Disposable Interface Implementation. ----------------------------------------

  /**
   * Returns <tt>true</tt> if the object is disposed.
   *
   * @return <tt>true</tt> if the object is disposed
   */
  public final boolean isDisposed() {
    return disposed;
  }

  /**
   * Disposes the object.
   */
  public final void dispose() {
    if (!disposed) {
      disposed = true;
      disposeObject();
      if (listenerCount > 0) {
        DisposeEvent e = new DisposeEvent(this);
        final DisposeListener[] listeners = this.listeners;
        for (int i = listenerCount; --i >= 0;) {
          listeners[i].objectDisposed(e);
        }
      }
    }
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  protected final void finalize() {
    dispose(); // NOPMD NOFB
  }

// Private Static Native Method. -----------------------------------------------

  /*!
   * @cute4j.native
   *    code="QObjectDelegate::initialize(env, cls);"
   */
  private static native void _initialize();

}
